package com.gitee.wsl.transform.validator.strings

import arrow.core.Either
import arrow.core.leftNel
import arrow.core.right
import com.gitee.wsl.transform.validator.ValidatorWithTransform
import com.gitee.wsl.transform.validator.func.filter
import com.gitee.wsl.transform.validator.func.map
import com.gitee.wsl.transform.validator.func.mapIfNotNull
import com.gitee.wsl.transform.validator.transformEither
import kotlin.jvm.JvmName


/**
 * Modifies the output of a String producing [ValidatorWithTransform] by trimming the output string
 * to remove prefix and suffix whitespace.
 *
 * @return the output of the underlying parser with whitespace trimmed.
 */
fun <I, E> ValidatorWithTransform<I, String, E>.trim(): ValidatorWithTransform<I, String, E> = map { it.trim() }

/**
 * Modifies the output of a nullable String producing [ValidatorWithTransform] by trimming the output string
 * to remove prefix and suffix whitespace.
 *
 * @return the output of the underlying parser with whitespace trimmed.
 */
@JvmName("trimOnNullableString")
fun <I, E> ValidatorWithTransform<I, String?, E>.trim(): ValidatorWithTransform<I, String?, E> = mapIfNotNull { it.trim() }

/**
 * Modifies the output of a String producing [ValidatorWithTransform] to strip the given [chars].
 */
fun <I, E> ValidatorWithTransform<I, String, E>.strip(chars: CharArray): ValidatorWithTransform<I, String, E> =
   map { chars.fold(it) { acc, c -> acc.replace(c.toString(), "") } }

/**
 * Constrains a String producing parser to ensure it matches the given [regex].
 */
fun <I, E> ValidatorWithTransform<I, String, E>.match(regex: Regex, ifError: (String) -> E): ValidatorWithTransform<I, String, E> =
   filter({ it.matches(regex) }, ifError)

/**
 * Composes an existing String? producing [ValidatorWithTransform] to reject nulls as errors.
 * In addition, also rejects blank strings. Error messages are generated by
 * the given function [ifError].
 *
 * @param ifError the error generating function used if the input is null or blank.
 *
 * @return valid if the input string is not null and not blank, otherwise invalid
 */
fun <I, E> ValidatorWithTransform<I, String?, E>.notNullOrBlank(ifError: () -> E): ValidatorWithTransform<I, String, E> {
   return transformEither { if (it.isNullOrBlank()) ifError().leftNel() else it.right() }
}

/**
 * Wraps an existing I -> String? [ValidatorWithTransform] to reject blank strings,
 * with the error message generated by the given function [ifBlank].
 *
 * Note: This parser accepts null strings as valid. If you wish to reject null inputs as well,
 * then use nullOrNotBlank.
 *
 * @param ifBlank the error generating function
 *
 * @return invalid if the input string contains only whitespace, otherwise valid
 */
fun <I, E> ValidatorWithTransform<I, String?, E>.notBlank(ifBlank: () -> E): ValidatorWithTransform<I, String?, E> {
   return transformEither {
      when {
         it == null -> Either.Right(null)
         it.isBlank() -> ifBlank().leftNel()
         else -> it.right()
      }
   }
}

/**
 * Wraps an existing String -> String [ValidatorWithTransform] to reject blank strings,
 * with the error message generated by the given function [ifBlank].
 *
 * Nulls are acceptable and passed through.
 *
 * @param ifBlank the error generating function
 *
 * @return invalid if the input string contains only whitespace, otherwise valid
 */
fun <I, E> ValidatorWithTransform<I, String?, E>.nullOrNotBlank(ifBlank: () -> E): ValidatorWithTransform<I, String?, E> {
   return transformEither {
      if (it == null) null.right() else if (it.isBlank()) ifBlank().leftNel() else it.right()
   }
}
